#include "Renderer.h"

CRenderer::CRenderer(void)
{
	m_nScreenWidth = 0;
	m_nScreenHeight = 0;
	m_bWindowed = false;
	m_pDev = NULL;
	m_pD3D = NULL;
	m_Error = S_OK;
	memset(&m_D3DPP, 0, sizeof(D3DPRESENT_PARAMETERS));
	m_fFOV = 0.0f;
	m_fAspectRatio = 0.0f;
	m_fNearPlane = 0.0f;
	m_fFarPlane = 0.0f;
	m_phCameraView = PHVEC3(0.0f, 0.0f, 0.0f);

	// TBR
	D3DXMatrixIdentity(&m_phTransform);
	D3DXMatrixTranslation(&m_phTransform, 0.0f, -1.0f, 2.0f);
	
	m_phSamusID = PH_INVALID_MODEL_ID;

	m_pAssetManager = NULL;
	m_pFontManager = NULL;
}

CRenderer::~CRenderer(void)
{
	Shutdown();
}

bool CRenderer::Initialize(float fFOV, float fNearPlane, float fFarPlane, PHVEC3 phCameraView)
{
	// Save Settings
	m_fFOV = fFOV;
	m_fAspectRatio = (float)m_nScreenWidth / (float)m_nScreenHeight;
	m_fNearPlane = fNearPlane;
	m_fFarPlane = fFarPlane;
	m_phCameraView = phCameraView;

	// Initialize Camera Settings
	m_cCamera.BuildPerspective(m_fFOV, m_fAspectRatio, m_fNearPlane, m_fFarPlane);
	m_cCamera.ViewTranslateLocal(m_phCameraView);

	m_cTimer.Reset();

	// Initialize Render Settings
	m_pDev->SetRenderState(D3DRS_LIGHTING, false);

	// Turn on ambient lighting 
	m_pDev->SetRenderState( D3DRS_AMBIENT, 0xffffffff );

// 	D3DXCreateSprite(m_pDev, &m_pSpriteManager);
// 	//TODO: Initialize your Font COM object
// 	D3DXCreateFont(m_pDev, 16, 0, FW_NORMAL, NULL, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Arial", &m_pFontManager);

	m_pAssetManager = CAssetManager::GetInstance();
	m_pAssetManager->Initialize(m_pDev);
	m_phSamusID = m_pAssetManager->AddModel("Dwarf/Dwarf.x");
	m_pFontManager = CFontManager::GetInstance();
	m_pFontManager->Initialize(m_pDev);

	return true;
}

bool CRenderer::InitD3D(HWND hWnd, HINSTANCE hInstance, int nScreenWidth, int nScreenHeight, bool bWindowed)
{
	// Save Window Settings
	m_hwnd = hWnd;
	m_nScreenWidth = nScreenWidth;
	m_nScreenHeight = nScreenHeight;
	m_bWindowed = bWindowed;
	
	// Create the Direct3D Device
	m_pD3D = Direct3DCreate9(D3D_SDK_VERSION);

	if(!m_pD3D)
	{
		MessageBoxA(NULL, "Failed to initialize Direct3D Device","Renderer Error", MB_OK);
		return false;
	}

	// Declare the Present Parameters for the game
	// Set the Parameters
	m_D3DPP.hDeviceWindow = m_hwnd;
	m_D3DPP.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

	m_D3DPP.BackBufferFormat = (bWindowed) ? D3DFMT_UNKNOWN : D3DFMT_X8R8G8B8;
	m_D3DPP.BackBufferCount = 1;
	m_D3DPP.BackBufferWidth = nScreenWidth;
	m_D3DPP.BackBufferHeight = nScreenHeight;

	m_D3DPP.Windowed = m_bWindowed;
	m_D3DPP.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
	m_D3DPP.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
	m_D3DPP.SwapEffect = D3DSWAPEFFECT_DISCARD;

	// Add for 3D, depth /stencil, stencil format
	m_D3DPP.EnableAutoDepthStencil = true;
	m_D3DPP.AutoDepthStencilFormat = D3DFMT_D16;

	// Set up and create the device using those parameters
	m_Error = m_pD3D->CreateDevice(0, D3DDEVTYPE_HAL, m_hwnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &m_D3DPP, &m_pDev);

	if(!m_pDev)
	{
		MessageBoxA(NULL, "Failed to initialize Device","Renderer Error", MB_OK);
		return false;
	}

	m_bInitialized = true;
	return m_bInitialized;
}

bool CRenderer::Update()
{
	m_pFontManager->QueueText(m_cTimer.GetFPSString().c_str());
	//m_pFontManager->QueueText(m_cTimer.GetFPSString().c_str(), -1, 0.0f, 0.0f, 200.0f, 200.0f);
	m_pFontManager->QueueText("Hello World", -1, RandomFloat(0.0f, 200.0f), RandomFloat(0.0f, 200.0f));
	m_pFontManager->QueueText("Hello World", -1, RandomFloat(0.0f, 200.0f), RandomFloat(0.0f, 200.0f));
	m_pFontManager->QueueText("Hello World", -1, RandomFloat(0.0f, 200.0f), RandomFloat(0.0f, 200.0f));
	m_pFontManager->QueueText("Hello World", -1, RandomFloat(0.0f, 200.0f), RandomFloat(0.0f, 200.0f));
	m_pFontManager->QueueText("Hello World", -1, RandomFloat(0.0f, 200.0f), RandomFloat(0.0f, 200.0f));

	m_cTimer.Update();

	return UpdateCamera();
}

void CRenderer::Render()
{
	m_pDev->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 125), 1.0f, 0);
	m_pDev->BeginScene();

	RenderX(m_phSamusID);
	RenderText();

	m_pDev->EndScene();
	m_pDev->Present(0,0,0,0);
}

void CRenderer::RenderX(PhModelID _phID)
{
	CXModel* _pXModel = (CXModel*)m_pAssetManager->GetModel(_phID);

	m_pDev->SetTransform(D3DTS_VIEW, &(m_cCamera.GetViewMatrix() * m_cCamera.GetProjectionMatrix()));
	m_pDev->SetTransform(D3DTS_VIEW, &m_cCamera.GetProjectionViewMatrix());
	m_pDev->SetTransform(D3DTS_WORLD, &(m_phTransform));

	// Meshes are divided into subsets, one for each material. Render them in a loop
	for(DWORD i = 0; i < _pXModel->GetNumMaterials(); i++)
	{
		// Set the material and texture for this subset
		m_pDev->SetMaterial(&_pXModel->GetMaterials()[i]);
		m_pDev->SetTexture(0, _pXModel->GetTextures()[i]);

		// Draw the mesh subset
		(*_pXModel->GetMesh())->DrawSubset(i);
	}
}

void CRenderer::RenderText(void)
{
	tQueuedText* _pQueued = m_pFontManager->GetQueuedText();
	ID3DXFont* _pFont = m_pFontManager->GetFont(PH_FONT_ARIAL_ID);
	ID3DXSprite* _pFontSurface = NULL;//m_pFontManager->GetSpriteSurface();

	if(_pFontSurface)
		_pFontSurface->Begin(D3DXSPRITE_ALPHABLEND | D3DXSPRITE_SORT_TEXTURE);

	while(_pQueued != NULL)
	{

		m_Error = _pFont->PreloadText(_pQueued->m_szText.c_str(), _pQueued->m_szText.length());

		if(_pQueued->m_bCalcRect)
			m_Error = _pFont->DrawTextA(_pFontSurface, _pQueued->m_szText.c_str(), _pQueued->m_phCount, &_pQueued->m_rRect, nsFont::F_DT_CALCRECT, _pQueued->m_phColor);

		m_Error = _pFont->DrawTextA(_pFontSurface, _pQueued->m_szText.c_str(), _pQueued->m_phCount, &_pQueued->m_rRect, _pQueued->m_dwFormat, _pQueued->m_phColor);


		_pQueued = m_pFontManager->GetQueuedText();
	}	

	if(_pFontSurface)
		_pFontSurface->End();
}


bool CRenderer::Shutdown()
{
	if(!m_bInitialized)
		return false;

	m_pFontManager->Shutdown();
	m_pFontManager = NULL;

	m_pAssetManager->Shutdown();
	m_pAssetManager = NULL;

	SAFE_RELEASE(m_pDev);
	SAFE_RELEASE(m_pD3D);

	m_nScreenWidth = 0;
	m_nScreenHeight = 0;
	m_bWindowed = false;
	m_Error = S_OK;
	memset(&m_D3DPP, 0, sizeof(D3DPRESENT_PARAMETERS));
	m_fFOV = 0.0f;
	m_fAspectRatio = 0.0f;
	m_fNearPlane = 0.0f;
	m_fFarPlane = 0.0f;
	m_phCameraView = PHVEC3(0.0f, 0.0f, 0.0f);

	return !m_bInitialized;
}

//////////////////////////////////////////////////////////////////////////
// Camera Controls
bool CRenderer::UpdateCamera(void)
{
	//////////////////////////////////////////////////////////////////////////
	// Translate Camera Up/Down
	if(GetAsyncKeyState(VK_UP) && !GetAsyncKeyState(VK_DOWN))
		m_cCamera.ViewTranslateLocalY(0.002f);
	else if(GetAsyncKeyState(VK_DOWN) && !GetAsyncKeyState(VK_UP))
		m_cCamera.ViewTranslateLocalY(-0.002f);

	//////////////////////////////////////////////////////////////////////////
	// Translate Camera Left/Right
	if(GetAsyncKeyState(VK_LEFT) && !GetAsyncKeyState(VK_RIGHT))
		m_cCamera.ViewTranslateLocalX(-0.002f);
	else if(GetAsyncKeyState(VK_RIGHT) && !GetAsyncKeyState(VK_LEFT))
		m_cCamera.ViewTranslateLocalX(0.002f);

	//////////////////////////////////////////////////////////////////////////
	// Translate Camera In/Out
	if(GetAsyncKeyState(VK_ADD) && !GetAsyncKeyState(VK_SUBTRACT))
		m_cCamera.ViewTranslateLocalZ(-0.002f);
	else if(GetAsyncKeyState(VK_SUBTRACT) && !GetAsyncKeyState(VK_ADD))
		m_cCamera.ViewTranslateLocalZ(0.002f);
	
	if(GetAsyncKeyState(VK_RETURN))
	{
		m_phTransform = m_cCamera.GetViewMatrix();
		D3DXMatrixTranslation(&m_phTransform, 0.0f, -3.0f, -8.0f);
	}

	if(GetAsyncKeyState(VK_ESCAPE))
		return false;

	return true;
}

//////////////////////////////////////////////////////////////////////////
// Device Functions
void CRenderer::CreateDevice()
{

}

void CRenderer::OnDeviceLost()
{
	if(!m_pAssetManager->Reload())
		Shutdown();
}

void CRenderer::ResetDevice()
{

}
